﻿// <file>
//     <copyright see="prj:///doc/copyright.txt"/>
//     <license see="prj:///doc/license.txt"/>
//     <owner name="Mike Krüger" email="mike@icsharpcode.net"/>
//     <version>$Revision$</version>
// </file>

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Text;
using System.Text;
using System.Windows.Forms;

using ICSharpCode.TextEditor.Actions;
using ICSharpCode.TextEditor.Document;
using ICSharpCode.TextEditor.Gui.CompletionWindow;

namespace ICSharpCode.TextEditor
{
	public delegate bool KeyEventHandler(char ch);
	public delegate bool DialogKeyProcessor(Keys keyData);
	
	/// <summary>
	/// This class paints the textarea.
	/// </summary>
	[ToolboxItem(false)]
	public class TextArea : Control
	{
        bool disposed;

        bool toolTipActive;

        bool hiddenMouseCursor = false;

        bool mouseDownState = false;

        /// <summary>
        /// Rectangle in text area that caused the current tool tip.
        /// Prevents tooltip from re-showing when it was closed because of a click or keyboard
        /// input and the mouse was not used.
        /// </summary>
        Rectangle toolTipRectangle;

        /// <summary>
        /// The position where the mouse cursor was when it was hidden. Sometimes the text editor gets MouseMove
        /// events when typing text even if the mouse is not moved.
        /// </summary>
        Point mouseCursorHidePosition;
		
		Point virtualTop        = new Point(0, 0);

        //VScrollBar vScrollBar = new VScrollBar();
        //HScrollBar hScrollBar = new HScrollBar();

        TextEditorScrollBar     vScrollBar;
        TextEditorScrollBar     hScrollBar;

        TextAreaControl         motherTextAreaControl;
		TextEditorControl       motherTextEditorControl;       
		
		List<BracketHighlightingSheme> bracketshemes  = new List<BracketHighlightingSheme>();
		TextAreaClipboardHandler  textAreaClipboardHandler;
		bool autoClearSelection = false;
        bool doHandleMousewheel = true;
		
		List<AbstractMargin> leftMargins = new List<AbstractMargin>();
		
		TextView      textView;
		GutterMargin  gutterMargin;
		FoldMargin    foldMargin;
		IconBarMargin iconBarMargin;
		
		SelectionManager selectionManager;
		Caret            caret;

        int FirstPhysicalLine
        {
            get
            {
                return VirtualTop.Y / textView.FontHeight;
            }
        }

        internal TextEditorScrollBar VScrollBar
        {
            get
            {
                return vScrollBar;
            }
        }

        internal TextEditorScrollBar HScrollBar
        {
            get
            {
                return hScrollBar;
            }
        }
        
        internal Point mousepos = new Point(0, 0);

        internal ImageLayout ImageLayout
        {
            get
            {
                return motherTextEditorControl.ImageLayout;
            }
        }

        internal Image backgroundImage
        {
            get
            {
                return motherTextEditorControl.BackgroundImage;
            }
        }

        static string oldToolTip;

        [Browsable(false)]
        public bool MouseDownState
        {
            get
            {
                return mouseDownState;
            }
        }

        [Browsable(false)]
		public IList<AbstractMargin> LeftMargins {
			get {
				return leftMargins.AsReadOnly();
			}
		}
		
		public void InsertLeftMargin(int index, AbstractMargin margin)
		{
			leftMargins.Insert(index, margin);
			Refresh();
		}
		
		public TextEditorControl MotherTextEditorControl {
			get {
				return motherTextEditorControl;
			}
		}
		
		public TextAreaControl MotherTextAreaControl {
			get {
				return motherTextAreaControl;
			}
		}
		
		public SelectionManager SelectionManager {
			get {
				return selectionManager;
			}
		}
		
		public Caret Caret {
			get {
				return caret;
			}
		}
		
		public TextView TextView {
			get {
				return textView;
			}
		}
		
		public GutterMargin GutterMargin {
			get {
				return gutterMargin;
			}
		}
		
		public FoldMargin FoldMargin {
			get {
				return foldMargin;
			}
		}
		
		public IconBarMargin IconBarMargin {
			get {
				return iconBarMargin;
			}
		}
		
		public Encoding Encoding {
			get {
				return motherTextEditorControl.Encoding;
			}
		}

        //public int MaxVScrollValue {
        //    get {
        //        return (Document.GetVisibleLine(Document.TotalNumberOfLines - 1) + 1 + TextView.VisibleLineCount * 2 / 3) * TextView.FontHeight;
        //    }
        //}

        //public int MaxVScrollValue
        //{
        //    get
        //    {
        //        //return (Document.GetVisibleLine(Document.TotalNumberOfLines - 1) + 1 + TextView.VisibleLineCount * 2 / 3) * TextView.FontHeight;
        //        int max = 0;
        //        //max = (Document.GetVisibleLine(Document.TotalNumberOfLines - 1)) * TextView.FontHeight;
        //        max = (Document.GetVisibleLine(Document.TotalNumberOfLines)) * TextView.FontHeight;
        //        return max;
        //    }
        //}

        public int MaxVScrollValue
        {
            get
            {
                int max = 0;
                if ((Document.GetVisibleLine(Document.TotalNumberOfLines) + 1) * TextView.FontHeight > TextView.DrawingPosition.Height)
                {
                    max = (Document.GetVisibleLine(Document.TotalNumberOfLines) + 1) * TextView.FontHeight - TextView.DrawingPosition.Height;
                }
                //max = (Document.GetVisibleLine(Document.TotalNumberOfLines) + 1) * TextView.FontHeight;
                //max = (Document.GetVisibleLine(Document.TotalNumberOfLines) + 1);
                return max;
            }
        }

        public int MaxHScrollValue
        {
            get
            {
                int max = 0;
                //foreach (LineSegment lineSegment in this.Document.LineSegmentCollection)
                //{
                //    int lineNumber = Document.GetLineNumberForOffset(lineSegment.Offset);
                //    if (Document.FoldingManager.IsLineVisible(lineNumber))
                //    {
                //        max = Math.Max(max, TextView.GetVisualColumnFast(lineSegment, lineSegment.Length));
                //    }
                //}
                for (int lineNumber = 0; lineNumber < (Document.GetVisibleLine(Document.TotalNumberOfLines)); lineNumber++)
                {
                    if (Document.FoldingManager.IsLineVisible(lineNumber))
                    {
                        LineSegment lineSegment = this.Document.GetLineSegment(lineNumber);
                        max = Math.Max(max, TextView.GetVisualColumnFast(lineSegment, lineSegment.Length));
                    }
                }
                if (max + 2 > TextView.VisibleColumnCount)
                {
                    max = max + 2 - TextView.VisibleColumnCount;
                }
                return max;
            }
        }
        		
		public Point VirtualTop {
			get {
				return virtualTop;
			}
			set {
				Point newVirtualTop = new Point(value.X, Math.Min(MaxVScrollValue, Math.Max(0, value.Y)));
                if (virtualTop != newVirtualTop)
                {
                    virtualTop = newVirtualTop;
                    motherTextAreaControl.VScrollBar.Value = virtualTop.Y;
                    Invalidate();
                    //Document.GuidelinesManager.Invalidate(this);
                    //Document.GuidelinesManager.CompleteUpdate(motherTextAreaControl.TextArea, false);
                }
				caret.UpdateCaretPosition();
			}
		}
		
		public bool AutoClearSelection {
			get {
				return autoClearSelection;
			}
			set {
				autoClearSelection = value;
			}
		}
		
		[Browsable(false)]
		public IDocument Document {
			get {
				return motherTextEditorControl.Document;
			}
		}
		
		public TextAreaClipboardHandler ClipboardHandler {
			get {
				return textAreaClipboardHandler;
			}
		}
				
		public ITextEditorProperties TextEditorProperties {
			get {
				return motherTextEditorControl.TextEditorProperties;
			}
		}

        public bool DoHandleMousewheel
        {
            get
            {
                return doHandleMousewheel;
            }
            set
            {
                doHandleMousewheel = value;
            }
        }
		
		public TextArea(TextEditorControl motherTextEditorControl, TextAreaControl motherTextAreaControl)
		{
			this.motherTextAreaControl      = motherTextAreaControl;
			this.motherTextEditorControl    = motherTextEditorControl;

            this.vScrollBar = motherTextAreaControl.VScrollBar;
            this.hScrollBar= motherTextAreaControl.HScrollBar;

            caret            = new Caret(this);
			selectionManager = new SelectionManager(Document, this);
			
			this.textAreaClipboardHandler = new TextAreaClipboardHandler(this);
			
			ResizeRedraw = true;
			
			SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
//			SetStyle(ControlStyles.AllPaintingInWmPaint, true);
//			SetStyle(ControlStyles.UserPaint, true);
			SetStyle(ControlStyles.Opaque, false);
			SetStyle(ControlStyles.ResizeRedraw, true);
			SetStyle(ControlStyles.Selectable, true);
			
			textView = new TextView(this);
			
			gutterMargin = new GutterMargin(this);
			foldMargin   = new FoldMargin(this);
			iconBarMargin = new IconBarMargin(this);

            leftMargins.AddRange(new AbstractMargin[] { iconBarMargin, gutterMargin, foldMargin });

            OptionsChanged();			
			
			new TextAreaMouseHandler(this).Attach();
			new TextAreaDragDropHandler().Attach(this);
			
			bracketshemes.Add(new BracketHighlightingSheme('{', '}'));
			bracketshemes.Add(new BracketHighlightingSheme('(', ')'));
			bracketshemes.Add(new BracketHighlightingSheme('[', ']'));
			
			caret.PositionChanged += new EventHandler(SearchMatchingBracket);
			Document.TextContentChanged += new EventHandler(TextContentChanged);
			Document.FoldingManager.FoldingsChanged += new EventHandler(DocumentFoldingsChanged);
		}
		
		public void UpdateMatchingBracket()
		{
			SearchMatchingBracket(null, null);
		}
		
		void TextContentChanged(object sender, EventArgs e)
		{
			Caret.Position = new TextLocation(0, 0);
			SelectionManager.SelectionCollection.Clear();
            Document.GuidelinesManager.Update(this);
		}

        void SearchMatchingBracket(object sender, EventArgs e)
		{
			if (!TextEditorProperties.ShowMatchingBracket) {
				textView.Highlight = null;
				return;
			}
			int oldLine1 = -1, oldLine2 = -1;
			if (textView.Highlight != null && textView.Highlight.OpenBrace.Y >=0 && textView.Highlight.OpenBrace.Y < Document.TotalNumberOfLines) {
				oldLine1 = textView.Highlight.OpenBrace.Y;
			}
			if (textView.Highlight != null && textView.Highlight.CloseBrace.Y >=0 && textView.Highlight.CloseBrace.Y < Document.TotalNumberOfLines) {
				oldLine2 = textView.Highlight.CloseBrace.Y;
			}
			textView.Highlight = FindMatchingBracketHighlight();
			if (oldLine1 >= 0)
				UpdateLine(oldLine1);
			if (oldLine2 >= 0 && oldLine2 != oldLine1)
				UpdateLine(oldLine2);
			if (textView.Highlight != null) {
				int newLine1 = textView.Highlight.OpenBrace.Y;
				int newLine2 = textView.Highlight.CloseBrace.Y;
				if (newLine1 != oldLine1 && newLine1 != oldLine2)
					UpdateLine(newLine1);
				if (newLine2 != oldLine1 && newLine2 != oldLine2 && newLine2 != newLine1)
					UpdateLine(newLine2);
			}
		}
		
		public Highlight FindMatchingBracketHighlight()
		{
			if (Caret.Offset == 0)
				return null;
			foreach (BracketHighlightingSheme bracketsheme in bracketshemes) {
				Highlight highlight = bracketsheme.GetHighlight(Document, Caret.Offset - 1);
				if (highlight != null) {
					return highlight;
				}
			}
			return null;
		}

        public int GetVisibleColumnCount()
        {
            int max = 0;
            foreach (LineSegment lineSegment in this.Document.LineSegmentCollection)
            {
                int lineNumber = Document.GetLineNumberForOffset(lineSegment.Offset);
                if (Document.FoldingManager.IsLineVisible(lineNumber))
                {
                    max = Math.Max(max, TextView.GetVisualColumnFast(lineSegment, lineSegment.Length));
                }
            }
            return max;
        }
		
		public void SetDesiredColumn()
		{
			Caret.DesiredColumn = TextView.GetDrawingXPos(Caret.Line, Caret.Column) + VirtualTop.X;
		}
		
		public void SetCaretToDesiredColumn()
		{
			FoldMarker dummy;
			Caret.Position = textView.GetLogicalColumn(Caret.Line, Caret.DesiredColumn + VirtualTop.X, out dummy);
		}
		
		public void OptionsChanged()
		{
			UpdateMatchingBracket();            
			textView.OptionsChanged();            
			caret.RecreateCaret();
			caret.UpdateCaretPosition();             
            Document.GuidelinesManager.Update(this);
            Refresh();
		}

        AbstractMargin lastMouseInMargin;

        public void HandleMouseWheel(MouseEventArgs e)
        {
            if ((Control.ModifierKeys & Keys.Control) != 0 && TextEditorProperties.MouseWheelTextZoom)
            {
                if (e.Delta > 0)
                {
                    motherTextEditorControl.Font = new Font(motherTextEditorControl.Font.Name,
                                                            motherTextEditorControl.Font.Size + 1);
                }
                else
                {
                    motherTextEditorControl.Font = new Font(motherTextEditorControl.Font.Name,
                                                            Math.Max(6, motherTextEditorControl.Font.Size - 1));
                }                
            }
            else
            {
                int newValue;
                int multiplier = Math.Abs(e.Delta) / NativeMethods.WHEEL_MAX_DELTA;

                if (System.Windows.Forms.SystemInformation.MouseWheelScrollLines > 0)
                {
                    newValue = this.vScrollBar.Value - (TextEditorProperties.MouseWheelScrollDown ? 1 : -1) * Math.Sign(e.Delta) * System.Windows.Forms.SystemInformation.MouseWheelScrollLines * vScrollBar.SmallChange * multiplier;
                }
                else
                {
                    newValue = this.vScrollBar.Value - (TextEditorProperties.MouseWheelScrollDown ? 1 : -1) * Math.Sign(e.Delta) * vScrollBar.LargeChange;
                }
                vScrollBar.Value = Math.Max(vScrollBar.Minimum, Math.Min(vScrollBar.Maximum, newValue));
            }
        }

        protected override void OnMouseWheel(MouseEventArgs e)
        {
            base.OnMouseWheel(e);
            if (DoHandleMousewheel)
            {
                HandleMouseWheel(e);
            }
        }
		
		protected override void OnMouseLeave(System.EventArgs e)
		{
			base.OnMouseLeave(e);
			this.Cursor = Cursors.Default;
			if (lastMouseInMargin != null) {
				lastMouseInMargin.HandleMouseLeave(EventArgs.Empty);
				lastMouseInMargin = null;
			}
			CloseToolTip();
		}
		
		protected override void OnMouseDown(System.Windows.Forms.MouseEventArgs e)
		{
			// this corrects weird problems when text is selected,
			// then a menu item is selected, then the text is
			// clicked again - it correctly synchronises the
			// click position
			mousepos = new Point(e.X, e.Y);

            if (e.Button == MouseButtons.Left)
                mouseDownState = true;

            foreach (AbstractMargin margin in leftMargins)
            {
                if (margin.DrawingPosition.Contains(e.X, e.Y))
                {
                    margin.HandleMouseDown(new Point(e.X, e.Y), e.Button);
                }
            }

            base.OnMouseDown(e);
			CloseToolTip();		
			
		}

        protected override void OnMouseUp(MouseEventArgs e)
        {
            mouseDownState = false;

            //foreach (AbstractMargin margin in leftMargins)
            //{
            //    if (margin.DrawingPosition.Contains(e.X, e.Y))
            //    {
            //        margin.HandleMouseDown(new Point(e.X, e.Y), e.Button);
            //    }
            //}

            base.OnMouseUp(e);
        }

        /// <summary>
        /// Shows the mouse cursor if it has been hidden.
        /// </summary>
        /// <param name="forceShow"><c>true</c> to always show the cursor or <c>false</c> to show it only if it has been moved since it was hidden.</param>
        internal void ShowHiddenCursor(bool forceShow)
		{
			if (hiddenMouseCursor) {
				if (mouseCursorHidePosition != Cursor.Position || forceShow) {
					Cursor.Show();
					hiddenMouseCursor = false;
				}
			}
		}
				
		// static because the mouse can only be in one text area and we don't want to have
		// tooltips of text areas from inactive tabs floating around.
		static DeclarationViewWindow toolTip;

		void SetToolTip(string text, int lineNumber)
		{
			if (toolTip == null || toolTip.IsDisposed)
				toolTip = new DeclarationViewWindow(this.FindForm());
			if (oldToolTip == text)
				return;
			if (text == null) {
				toolTip.Hide();
			} else {
				Point p = Control.MousePosition;
				Point cp = PointToClient(p);
				if (lineNumber >= 0) {
					lineNumber = this.Document.GetVisibleLine(lineNumber);
					p.Y = (p.Y - cp.Y) + (lineNumber * this.TextView.FontHeight) - this.virtualTop.Y;
				}
				p.Offset(3, 3);
				toolTip.Owner = this.FindForm();
				toolTip.Location = p;
				toolTip.Description = text;
				toolTip.HideOnClick = true;
				toolTip.Show();
			}
			oldToolTip = text;
		}
		
		public event ToolTipRequestEventHandler ToolTipRequest;
		
		protected virtual void OnToolTipRequest(ToolTipRequestEventArgs e)
		{
			if (ToolTipRequest != null) {
				ToolTipRequest(this, e);
			}
		}
		
		protected virtual void CloseToolTip()
		{
			if (toolTipActive) {
				//Console.WriteLine("Closing tooltip");
				toolTipActive = false;
				SetToolTip(null, -1);
			}
			ResetMouseEventArgs();
		}
		
		protected override void OnMouseHover(EventArgs e)
		{
			base.OnMouseHover(e);
			//Console.WriteLine("Hover raised at " + PointToClient(Control.MousePosition));
			if (MouseButtons == MouseButtons.None) {
				RequestToolTip(PointToClient(Control.MousePosition));
			} else {
				CloseToolTip();
			}
		}
		
		protected void RequestToolTip(Point mousePos)
		{
			if (toolTipRectangle.Contains(mousePos)) {
				if (!toolTipActive)
					ResetMouseEventArgs();
				return;
			}
			
			//Console.WriteLine("Request tooltip for " + mousePos);
			
			toolTipRectangle = new Rectangle(mousePos.X - 4, mousePos.Y - 4, 8, 8);
			
			TextLocation logicPos = textView.GetLogicalPosition(mousePos.X - textView.DrawingPosition.Left,
			                                                    mousePos.Y - textView.DrawingPosition.Top);
			bool inDocument = textView.DrawingPosition.Contains(mousePos)
				&& logicPos.Y >= 0 && logicPos.Y < Document.TotalNumberOfLines;
			ToolTipRequestEventArgs args = new ToolTipRequestEventArgs(mousePos, logicPos, inDocument);
			OnToolTipRequest(args);
			if (args.ToolTipShown) {
				//Console.WriteLine("Set tooltip to " + args.toolTipText);
				toolTipActive = true;
				SetToolTip(args.toolTipText, inDocument ? logicPos.Y + 1 : -1);
			} else {
				CloseToolTip();
			}
		}
		
		// external interface to the attached event
		internal void RaiseMouseMove(MouseEventArgs e)
		{
			OnMouseMove(e);
		}

        internal void UpdateVirtualTop(Point value)
        {
            Point newVirtualTop = new Point(value.X, Math.Min(MaxVScrollValue, Math.Max(0, value.Y)));
            if (virtualTop != newVirtualTop)
            {
                virtualTop = newVirtualTop;
                motherTextAreaControl.VScrollBar.Value = virtualTop.Y;
                Invalidate();
                //Document.GuidelinesManager.Invalidate(this);
                //Document.GuidelinesManager.CompleteUpdate(motherTextAreaControl.TextArea, false);
            }
            caret.UpdateCaretPosition();
        }

		protected override void OnMouseMove(MouseEventArgs e)
		{
			base.OnMouseMove(e);
			if (!toolTipRectangle.Contains(e.Location)) {
				toolTipRectangle = Rectangle.Empty;
				if (toolTipActive)
					RequestToolTip(e.Location);
			}

            if (Document.TextEditorProperties.ShowLineNumbers) {  
                foreach (AbstractMargin margin in leftMargins) {
                    if (margin.DrawingPosition.Contains(e.X, e.Y)) {
                        this.Cursor = margin.Cursor;
                        margin.HandleMouseMove(new Point(e.X, e.Y), e.Button);
                        if (lastMouseInMargin != margin) {
                            if (lastMouseInMargin != null) {
                                lastMouseInMargin.HandleMouseLeave(EventArgs.Empty);
                            }
                            lastMouseInMargin = margin;
                        }
                        return;
                    }
                }
			}
			if (lastMouseInMargin != null) {
				lastMouseInMargin.HandleMouseLeave(EventArgs.Empty);
				lastMouseInMargin = null;
			}
			if (textView.DrawingPosition.Contains(e.X, e.Y)) {                 
                TextLocation realmousepos = TextView.GetLogicalPosition(e.X - TextView.DrawingPosition.X, e.Y - TextView.DrawingPosition.Y);
                if (SelectionManager.IsSelected(Document.PositionToOffset(realmousepos)) && MouseButtons == MouseButtons.None)
                {
                    // mouse is hovering over a selection, so show default mouse
                    if (this.Cursor != Cursors.Default)
                        this.Cursor = Cursors.Default;
                }
                else
                {
                    // mouse is hovering over text area, not a selection, so show the textView cursor
                    if (this.Cursor != textView.Cursor)
                        this.Cursor = textView.Cursor;
                }
                return;

            }
			this.Cursor = Cursors.Default;
		}

		AbstractMargin updateMargin = null;
		
		public void Refresh(AbstractMargin margin)
		{
			updateMargin = margin;
			Invalidate(updateMargin.DrawingPosition);
			Update();
			updateMargin = null;
		}

        public void DrawImage(Graphics g)
        {
            int currentXPos = 0;
            int currentYPos = 0;
            bool adjustScrollBars = false;
            //Graphics g = e.Graphics;
            //Rectangle clipRectangle = e.ClipRectangle;

            Rectangle clipRectangle = new Rectangle(0, 0, this.Width, this.Height); 

            if (this.backgroundImage != null)
            {
                if (this.ImageLayout == ImageLayout.None)
                {
                    int x = this.Width - this.backgroundImage.Width;
                    int y = (this.Height - this.backgroundImage.Height);

                    int w = this.backgroundImage.Width;
                    int h = this.backgroundImage.Height;

                    Rectangle backgroundImageRectangle = new Rectangle(x, y, w, h);
                    g.DrawImage(this.backgroundImage, backgroundImageRectangle);

                }
                else
                {
                    g.DrawImage(this.backgroundImage, this.ClientRectangle,
                        new Rectangle(0, 0, this.backgroundImage.Width, this.backgroundImage.Height), GraphicsUnit.Pixel);
                }
            }

            bool isFullRepaint = clipRectangle.X == 0 && clipRectangle.Y == 0
                && clipRectangle.Width == this.Width && clipRectangle.Height == this.Height;

            g.TextRenderingHint = this.TextEditorProperties.TextRenderingHint;

            if (updateMargin != null)
            {
                updateMargin.Paint(g, updateMargin.DrawingPosition);
                //clipRectangle.Intersect(updateMargin.DrawingPosition);
            }

            if (clipRectangle.Width <= 0 || clipRectangle.Height <= 0)
            {
                return;
            }

            foreach (AbstractMargin margin in leftMargins)
            {
                if (margin.IsVisible)
                {
                    Rectangle marginRectangle = new Rectangle(currentXPos, currentYPos, margin.Size.Width, Height - currentYPos);
                    if (marginRectangle != margin.DrawingPosition)
                    {
                        // margin changed size
                        if (!isFullRepaint && !clipRectangle.Contains(marginRectangle))
                        {
                            Invalidate(); // do a full repaint
                        }
                        adjustScrollBars = true;
                        margin.DrawingPosition = marginRectangle;
                    }
                    currentXPos += margin.DrawingPosition.Width;
                    if (clipRectangle.IntersectsWith(marginRectangle))
                    {
                        marginRectangle.Intersect(clipRectangle);
                        if (!marginRectangle.IsEmpty)
                        {
                            margin.Paint(g, marginRectangle);
                        }
                    }
                }
                else
                {
                    if (margin.GetType() == typeof(FoldMargin))
                        currentXPos += 8;
                }
            }

            Rectangle textViewArea = new Rectangle(currentXPos, currentYPos, Width - currentXPos, Height - currentYPos);
            if (textViewArea != textView.DrawingPosition)
            {
                adjustScrollBars = true;
                textView.DrawingPosition = textViewArea;
                // update caret position (but outside of WM_PAINT!)
                BeginInvoke((MethodInvoker)caret.UpdateCaretPosition);
            }
            if (clipRectangle.IntersectsWith(textViewArea))
            {
                textViewArea.Intersect(clipRectangle);
                if (!textViewArea.IsEmpty)
                {
                    textView.Paint(g, textViewArea);
                }
            }

            if (adjustScrollBars)
            {
                this.motherTextAreaControl.AdjustScrollBars();
            }
        }

        protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
        {
            int currentXPos = 0;
            int currentYPos = 0;
            bool adjustScrollBars = false;
            Graphics g = e.Graphics;
            Rectangle clipRectangle = e.ClipRectangle;

            if (this.backgroundImage != null)
            {
                if (this.ImageLayout == ImageLayout.None)
                {
                    int x = this.Width - this.backgroundImage.Width;
                    int y = (this.Height - this.backgroundImage.Height);

                    int w = this.backgroundImage.Width;
                    int h = this.backgroundImage.Height;

                    Rectangle backgroundImageRectangle = new Rectangle(x, y, w, h);
                    g.DrawImage(this.backgroundImage, backgroundImageRectangle);

                }
                else
                {
                    g.DrawImage(this.backgroundImage, this.ClientRectangle,
                        new Rectangle(0, 0, this.backgroundImage.Width, this.backgroundImage.Height), GraphicsUnit.Pixel);
                }
            }

            bool isFullRepaint = clipRectangle.X == 0 && clipRectangle.Y == 0
                && clipRectangle.Width == this.Width && clipRectangle.Height == this.Height;

            g.TextRenderingHint = this.TextEditorProperties.TextRenderingHint;

            if (updateMargin != null)
            {
                updateMargin.Paint(g, updateMargin.DrawingPosition);
                //clipRectangle.Intersect(updateMargin.DrawingPosition);
            }

            if (clipRectangle.Width <= 0 || clipRectangle.Height <= 0)
            {
                return;
            }

            foreach (AbstractMargin margin in leftMargins)
            {
                if (margin.IsVisible)
                {
                    Rectangle marginRectangle = new Rectangle(currentXPos, currentYPos, margin.Size.Width, Height - currentYPos);
                    if (marginRectangle != margin.DrawingPosition)
                    {
                        // margin changed size
                        if (!isFullRepaint && !clipRectangle.Contains(marginRectangle))
                        {
                            Invalidate(); // do a full repaint
                        }
                        adjustScrollBars = true;
                        margin.DrawingPosition = marginRectangle;
                    }
                    currentXPos += margin.DrawingPosition.Width;
                    if (clipRectangle.IntersectsWith(marginRectangle))
                    {
                        marginRectangle.Intersect(clipRectangle);
                        if (!marginRectangle.IsEmpty)
                        {
                            margin.Paint(g, marginRectangle);
                        }
                    }
                }
                else
                {
                    if (margin.GetType() == typeof(FoldMargin))
                        currentXPos += 8;
                }
            }            

            Rectangle textViewArea = new Rectangle(currentXPos, currentYPos, Width - currentXPos, Height - currentYPos);
            if (textViewArea != textView.DrawingPosition)
            {
                adjustScrollBars = true;
                textView.DrawingPosition = textViewArea;
                // update caret position (but outside of WM_PAINT!)
                BeginInvoke((MethodInvoker)caret.UpdateCaretPosition);
            }
            if (clipRectangle.IntersectsWith(textViewArea))
            {
                textViewArea.Intersect(clipRectangle);
                if (!textViewArea.IsEmpty)
                {
                    textView.Paint(g, textViewArea);
                }
            }

            if (adjustScrollBars)
            {
                this.motherTextAreaControl.AdjustScrollBars();
            }

            base.OnPaint(e);

        }

        void DocumentFoldingsChanged(object sender, EventArgs e)
        {
            Document.GuidelinesManager.Clear(this);
            Caret.UpdateCaretPosition();
            Invalidate();
            motherTextAreaControl.AdjustScrollBars();
            Document.GuidelinesManager.Update(this);
        }
		
		#region keyboard handling methods
		
		/// <summary>
		/// This method is called on each Keypress
		/// </summary>
		/// <returns>
		/// True, if the key is handled by this method and should NOT be
		/// inserted in the textarea.
		/// </returns>
		protected internal virtual bool HandleKeyPress(char ch)
		{
			if (KeyEventHandler != null) {
				return KeyEventHandler(ch);
			}
			return false;
		}
		
		// Fixes SD2-747: Form containing the text editor and a button with a shortcut
		protected override bool IsInputChar(char charCode)
		{
			return true;
		}
		
		internal bool IsReadOnly(int offset)
		{
			if (Document.ReadOnly) {
				return true;
			}
			if (TextEditorProperties.SupportReadOnlySegments) {
				return Document.MarkerStrategy.GetMarkers(offset).Exists(m=>m.IsReadOnly);
			} else {
				return false;
			}
		}
		
		internal bool IsReadOnly(int offset, int length)
		{
			if (Document.ReadOnly) {
				return true;
			}
			if (TextEditorProperties.SupportReadOnlySegments) {
				return Document.MarkerStrategy.GetMarkers(offset, length).Exists(m=>m.IsReadOnly);
			} else {
				return false;
			}
		}
		
		public void SimulateKeyPress(char ch)
		{
			if (SelectionManager.HasSomethingSelected) {
				if (SelectionManager.SelectionIsReadonly)
					return;
			} else if (IsReadOnly(Caret.Offset)) {
				return;
			}
			
			if (ch < ' ') {
				return;
			}
			
			if (!hiddenMouseCursor && TextEditorProperties.HideMouseCursor) {
				if (this.ClientRectangle.Contains(PointToClient(Cursor.Position))) {
					mouseCursorHidePosition = Cursor.Position;
					hiddenMouseCursor = true;
					Cursor.Hide();
				}
			}
			CloseToolTip();
			
			BeginUpdate();
			Document.UndoStack.StartUndoGroup();
			try {
				// INSERT char
				if (!HandleKeyPress(ch)) {
					switch (Caret.CaretMode) {
						case CaretMode.InsertMode:
							InsertChar(ch);
							break;
						case CaretMode.OverwriteMode:
							ReplaceChar(ch);
							break;
						default:
							Debug.Assert(false, "Unknown caret mode " + Caret.CaretMode);
							break;
					}
				}
				
				int currentLineNr = Caret.Line;
				Document.FormattingStrategy.FormatLine(this, currentLineNr, Document.PositionToOffset(Caret.Position), ch);
				
				EndUpdate();
			} finally {
				Document.UndoStack.EndUndoGroup();
			}
		}
		
		protected override void OnKeyPress(KeyPressEventArgs e)
		{
			base.OnKeyPress(e);
			SimulateKeyPress(e.KeyChar);
			e.Handled = true;
		}

        /// <summary>
        /// This method executes a dialog key
        /// </summary>
        public bool ExecuteDialogKey(Keys keyData)
        {
            // try, if a dialog key processor was set to use this
            if (DoProcessDialogKey != null && DoProcessDialogKey(keyData))
            {
                return true;
            }

            // if not (or the process was 'silent', use the standard edit actions
            IEditAction action = motherTextEditorControl.GetEditAction(keyData);
            AutoClearSelection = true;
            if (action != null)
            {
                BeginUpdate();
                try
                {
                    lock (Document)
                    {
                        action.Execute(this);                        
                        if( AutoClearSelection &&
                            !selectionManager.IsMutilSelect &&
                            selectionManager.HasSomethingSelected &&
                            Document.TextEditorProperties.DocumentSelectionMode == DocumentSelectionMode.Normal)
                        {
                            SelectionManager.ClearSelection();
                        }
                    }
                }
                finally
                {
                    EndUpdate();
                    Caret.UpdateCaretPosition();
                }
                return true;
            }
            return false;
        }
		
		protected override bool ProcessDialogKey(Keys keyData)
		{
            if (keyData == (Keys.Alt | Keys.Menu))
            {
                return ExecuteDialogKey(keyData) || true;
            }
			return ExecuteDialogKey(keyData) || base.ProcessDialogKey(keyData);
		}

		#endregion
		
		public void ScrollToCaret()
		{
			motherTextAreaControl.ScrollToCaret();
		}
		
		public void ScrollTo(int line)
		{
			motherTextAreaControl.ScrollTo(line);
		}
		
		public void BeginUpdate()
		{
			motherTextEditorControl.BeginUpdate();
		}
		
		public void EndUpdate()
		{
			motherTextEditorControl.EndUpdate();
		}
		
		public bool EnableCutOrPaste {
			get {
				if (motherTextAreaControl == null)
					return false;
				if (SelectionManager.HasSomethingSelected)
					return !SelectionManager.SelectionIsReadonly;
				else
					return !IsReadOnly(Caret.Offset);
			}
		}
		
		string GenerateWhitespaceString(int length)
		{
			return new String(' ', length);
		}

        /// <remarks>
        /// Inserts a single character at the caret position
        /// </remarks>
        public void InsertChar(char ch)
        {
            bool updating = motherTextEditorControl.IsInUpdate;
            if (!updating)
            {
                BeginUpdate();
            }

            // filter out forgein whitespace chars and replace them with standard space (ASCII 32)
            if (Char.IsWhiteSpace(ch) && ch != '\t' && ch != '\n')
            {
                ch = ' ';
            }

            Document.UndoStack.StartUndoGroup();


            if (SelectionManager.IsMutilSelect)
            {
                if (selectionManager.HasSomethingMutilLinesSeleted)
                {
                    Delete.DeleteSelection(this);
                }

                TextLocation startLocation = new TextLocation(SelectionManager.SelectionStart.Column, SelectionManager.SelectionStart.Line);
                TextLocation endLocation = new TextLocation(SelectionManager.SelectionEnd.Column, SelectionManager.SelectionEnd.Line);

                List<ISelection> selectionCollection = new List<ISelection>();
                foreach (ISelection selection in selectionManager.selectionCollection)
                {
                    TextLocation location1 = new TextLocation(selection.StartPosition.Column, selection.StartPosition.Line);
                    TextLocation location2 = new TextLocation(selection.EndPosition.Column, selection.EndPosition.Line);
                    selectionCollection.Add(new NewLineSelection(Document, location1, location2));
                }

                foreach (ISelection selection in selectionCollection)
                {
                    LineSegment caretLine = Document.GetLineSegment(selection.StartPosition.Line);

                    int offset = Document.PositionToOffset(selection.StartPosition);
                    // use desired column for generated whitespaces
                    int dc = selection.StartPosition.Column;
                    if (caretLine.Length < dc && ch != '\n')
                    {
                        Document.Insert(offset, GenerateWhitespaceString(dc - caretLine.Length) + ch);
                    }
                    else
                    {
                        Document.Insert(offset, ch.ToString());
                    }

                    selection.StartPosition = new TextLocation(selection.StartPosition.Column + 1, selection.StartPosition.Line);
                    selection.EndPosition = new TextLocation(selection.StartPosition.Column, selection.EndPosition.Line);

                }

                SelectionManager.SelectionStart = new TextLocation(SelectionManager.SelectionStart.Column + 1, SelectionManager.SelectionStart.Line);
                SelectionManager.SelectionEnd = new TextLocation(SelectionManager.SelectionEnd.Column + 1, SelectionManager.SelectionEnd.Line);

                SelectionManager.selectionCollection = selectionCollection;

            }
            else
            {
                if (Document.TextEditorProperties.DocumentSelectionMode == DocumentSelectionMode.Normal &&
                SelectionManager.SelectionCollection.Count > 0)
                {
                    Caret.Position = SelectionManager.SelectionCollection[0].StartPosition;
                    SelectionManager.RemoveSelectedText();
                }
                LineSegment caretLine = Document.GetLineSegment(Caret.Line);
                int offset = Caret.Offset;
                // use desired column for generated whitespaces
                int dc = Caret.Column;
                if (caretLine.Length < dc && ch != '\n')
                {
                    Document.Insert(offset, GenerateWhitespaceString(dc - caretLine.Length) + ch);
                }
                else
                {
                    Document.Insert(offset, ch.ToString());
                }
            }


            Document.UndoStack.EndUndoGroup();
            ++Caret.Column;

            if (!updating)
            {
                EndUpdate();
                UpdateLineToEnd(Caret.Line, Caret.Column);
            }

            // I prefer to set NOT the standard column, if you type something
            //			++Caret.DesiredColumn;
        }

        /// <remarks>
        /// Inserts a whole string at the caret position
        /// </remarks>
        public void InsertString(string str,bool paste=false)
        {
            bool updating = motherTextEditorControl.IsInUpdate;
            if (!updating)
            {
                BeginUpdate();
            }
            try
            {
                Document.UndoStack.StartUndoGroup();

                int oldOffset = 0;
                int oldLine = 0;

                if ( SelectionManager.IsMutilSelect)
                {
                    if (Document.TextEditorProperties.DocumentSelectionMode == DocumentSelectionMode.Normal &&
                                        SelectionManager.SelectionCollection.Count > 0)
                    {
                        TextLocation startLocation = new TextLocation(SelectionManager.SelectionStart.Column, SelectionManager.SelectionStart.Line);
                        TextLocation endLocation = new TextLocation(SelectionManager.SelectionEnd.Column, SelectionManager.SelectionEnd.Line);

                        if (str == "\t")
                        {
                            foreach (ISelection selection in SelectionManager.SelectionCollection)
                            {
                                Caret.Position = selection.StartPosition;

                                oldOffset = Document.PositionToOffset(Caret.Position);
                                oldLine = Caret.Line;

                                int startOffset = Document.PositionToOffset(selection.StartPosition);
                                int endOffset = Document.PositionToOffset(selection.EndPosition);

                                LineSegment caretLine = Document.GetLineSegment(Caret.Line);

                                if (caretLine.Length < Caret.Column)
                                {
                                    int whiteSpaceLength = Caret.Column - caretLine.Length;
                                    Document.Insert(oldOffset, GenerateWhitespaceString(whiteSpaceLength) + str);
                                    Caret.Position = Document.OffsetToPosition(oldOffset + str.Length + whiteSpaceLength);

                                    startOffset = startOffset + str.Length + whiteSpaceLength;
                                    endOffset = endOffset + str.Length + whiteSpaceLength;
                                }
                                else
                                {
                                    Document.Insert(oldOffset, str);
                                    Caret.Position = Document.OffsetToPosition(oldOffset + str.Length);

                                    startOffset = startOffset + str.Length;
                                    endOffset = endOffset + str.Length;
                                }

                                selection.StartPosition = new TextLocation(Document.OffsetToPosition(startOffset).Column, selection.StartPosition.Line);
                                selection.EndPosition = new TextLocation(Document.OffsetToPosition(endOffset).Column, selection.EndPosition.Line);

                                if (selection.StartPosition.Column > startLocation.Column)
                                    startLocation = new TextLocation(selection.StartPosition.Column, SelectionManager.SelectionStart.Line);
                                if (selection.EndPosition.Column > endLocation.Column)
                                    endLocation = new TextLocation(selection.EndPosition.Column, SelectionManager.SelectionEnd.Line);

                            }
                        }
                        else if (!paste && selectionManager.selectionCollection.Count > 0)
                        {
                            foreach (ISelection selection in SelectionManager.SelectionCollection)
                            {
                                Caret.Position = selection.StartPosition;

                                oldOffset = Document.PositionToOffset(Caret.Position);
                                oldLine = Caret.Line;

                                int startOffset = Document.PositionToOffset(selection.StartPosition);
                                int endOffset = Document.PositionToOffset(selection.EndPosition);

                                LineSegment caretLine = Document.GetLineSegment(Caret.Line);

                                if (caretLine.Length < Caret.Column)
                                {
                                    int whiteSpaceLength = Caret.Column - caretLine.Length;
                                    Document.Insert(oldOffset, GenerateWhitespaceString(whiteSpaceLength) + str);
                                    Caret.Position = Document.OffsetToPosition(oldOffset + str.Length + whiteSpaceLength);

                                    startOffset = startOffset + str.Length + whiteSpaceLength;
                                    endOffset = endOffset + str.Length + whiteSpaceLength;
                                }
                                else
                                {
                                    Document.Insert(oldOffset, str);
                                    Caret.Position = Document.OffsetToPosition(oldOffset + str.Length);

                                    startOffset = startOffset + str.Length;
                                    endOffset = endOffset + str.Length;
                                }

                                selection.StartPosition = new TextLocation(Document.OffsetToPosition(startOffset).Column, selection.StartPosition.Line);
                                selection.EndPosition = new TextLocation(Document.OffsetToPosition(endOffset).Column, selection.EndPosition.Line);

                                if (selection.StartPosition.Column > startLocation.Column)
                                    startLocation = new TextLocation(selection.StartPosition.Column, SelectionManager.SelectionStart.Line);
                                if (selection.EndPosition.Column > endLocation.Column)
                                    endLocation = new TextLocation(selection.EndPosition.Column, SelectionManager.SelectionEnd.Line);

                            }
                        }
                        else
                        {
                            oldOffset = Document.PositionToOffset(Caret.Position);
                            oldLine = Caret.Line;
                            LineSegment caretLine = Document.GetLineSegment(Caret.Line);
                            if (caretLine.Length < Caret.Column)
                            {
                                int whiteSpaceLength = Caret.Column - caretLine.Length;
                                Document.Insert(oldOffset, GenerateWhitespaceString(whiteSpaceLength) + str);
                                Caret.Position = Document.OffsetToPosition(oldOffset + str.Length + whiteSpaceLength);
                            }
                            else
                            {
                                Document.Insert(oldOffset, str);
                                Caret.Position = Document.OffsetToPosition(oldOffset + str.Length);
                            }

                            startLocation = new TextLocation(Caret.Position.Column, startLocation.Line);
                            endLocation = new TextLocation(Caret.Position.Column, endLocation.Line);

                        }

                        SelectionManager.SelectionStart = startLocation;
                        SelectionManager.SelectionEnd = endLocation;
                        
                    }
                }
                else
                {
                    if (Document.TextEditorProperties.DocumentSelectionMode == DocumentSelectionMode.Normal &&
                                        SelectionManager.SelectionCollection.Count > 0)
                    {
                        Caret.Position = SelectionManager.SelectionCollection[0].StartPosition;
                        SelectionManager.RemoveSelectedText();
                    }

                    oldOffset = Document.PositionToOffset(Caret.Position);
                    oldLine = Caret.Line;
                    LineSegment caretLine = Document.GetLineSegment(Caret.Line);
                    if (caretLine.Length < Caret.Column)
                    {
                        int whiteSpaceLength = Caret.Column - caretLine.Length;
                        Document.Insert(oldOffset, GenerateWhitespaceString(whiteSpaceLength) + str);
                        Caret.Position = Document.OffsetToPosition(oldOffset + str.Length + whiteSpaceLength);
                    }
                    else
                    {
                        Document.Insert(oldOffset, str);
                        Caret.Position = Document.OffsetToPosition(oldOffset + str.Length);
                    }
                }           

                Document.UndoStack.EndUndoGroup();
                if (oldLine != Caret.Line)
                {
                    UpdateToEnd(oldLine);
                }
                else
                {
                    UpdateLineToEnd(Caret.Line, Caret.Column);
                }
            }
            finally
            {
                if (!updating)
                {
                    EndUpdate();
                }
            }
        }

        /// <remarks>
        /// Replaces a char at the caret position
        /// </remarks>
        public void ReplaceChar(char ch)
        {
            bool updating = motherTextEditorControl.IsInUpdate;
            if (!updating)
            {
                BeginUpdate();
            }
            if (Document.TextEditorProperties.DocumentSelectionMode == DocumentSelectionMode.Normal && SelectionManager.SelectionCollection.Count > 0)
            {
                Caret.Position = SelectionManager.SelectionCollection[0].StartPosition;
                SelectionManager.RemoveSelectedText();
            }

            int lineNr = Caret.Line;
            LineSegment line = Document.GetLineSegment(lineNr);
            int offset = Document.PositionToOffset(Caret.Position);
            if (offset < line.Offset + line.Length)
            {
                Document.Replace(offset, 1, ch.ToString());
            }
            else
            {
                Document.Insert(offset, ch.ToString());
            }
            if (!updating)
            {
                EndUpdate();
                UpdateLineToEnd(lineNr, Caret.Column);
            }
            ++Caret.Column;
            //			++Caret.DesiredColumn;
        }
		
		protected override void Dispose(bool disposing)
		{
			base.Dispose(disposing);
			if (disposing) {
				if (!disposed) {
					disposed = true;
					if (caret != null) {
						caret.PositionChanged -= new EventHandler(SearchMatchingBracket);
						caret.Dispose();
					}
					if (selectionManager != null) {
						selectionManager.Dispose();
					}
					Document.TextContentChanged -= new EventHandler(TextContentChanged);
					Document.FoldingManager.FoldingsChanged -= new EventHandler(DocumentFoldingsChanged);
					motherTextAreaControl = null;
					motherTextEditorControl = null;
					foreach (AbstractMargin margin in leftMargins) {
						if (margin is IDisposable)
							(margin as IDisposable).Dispose();
					}
					textView.Dispose();
				}
			}
		}
		
		#region UPDATE Commands

		internal void UpdateLine(int line)
		{
			UpdateLines(0, line, line);
		}
		
		internal void UpdateLines(int lineBegin, int lineEnd)
		{
			UpdateLines(0, lineBegin, lineEnd);
		}
		
		internal void UpdateToEnd(int lineBegin)
		{
//			if (lineBegin > FirstPhysicalLine + textView.VisibleLineCount) {
//				return;
//			}
			
			lineBegin = Document.GetVisibleLine(lineBegin);
			int y         = Math.Max(    0, (int)(lineBegin * textView.FontHeight));
			y = Math.Max(0, y - this.virtualTop.Y);
			Rectangle r = new Rectangle(0,
			                            y,
			                            Width,
			                            Height - y);
			Invalidate(r);
		}
		
		internal void UpdateLineToEnd(int lineNr, int xStart)
		{
			UpdateLines(xStart, lineNr, lineNr);
		}
		
		internal void UpdateLine(int line, int begin, int end)
		{
			UpdateLines(line, line);
		}
		
		internal void UpdateLines(int xPos, int lineBegin, int lineEnd)
		{
//			if (lineEnd < FirstPhysicalLine || lineBegin > FirstPhysicalLine + textView.VisibleLineCount) {
//				return;
//			}
			
			InvalidateLines((int)(xPos * this.TextView.WideSpaceWidth), lineBegin, lineEnd);
		}
		
		void InvalidateLines(int xPos, int lineBegin, int lineEnd)
		{
			lineBegin     = Math.Max(Document.GetVisibleLine(lineBegin), FirstPhysicalLine);
			lineEnd       = Math.Min(Document.GetVisibleLine(lineEnd),   FirstPhysicalLine + textView.VisibleLineCount);
			int y         = Math.Max(    0, (int)(lineBegin  * textView.FontHeight));
			int height    = Math.Min(textView.DrawingPosition.Height, (int)((1 + lineEnd - lineBegin) * (textView.FontHeight + 1)));
			
			Rectangle r = new Rectangle(0,
			                            y - 1 - this.virtualTop.Y,
			                            Width,
			                            height + 3);
			
			Invalidate(r);
		}
		
        #endregion

		public event KeyEventHandler    KeyEventHandler;
		public event DialogKeyProcessor DoProcessDialogKey;

        internal object showContextMenuEventObject = new object();
        public event EventHandler ShowContextMenuHandler
        {
            add
            {
                Events.AddHandler(showContextMenuEventObject, value);
            }
            remove
            {
                Events.RemoveHandler(showContextMenuEventObject, value);
            }
        }

        internal void DoShowContextMenuHandler(EventArgs e)
        {
            EventHandler handler = (EventHandler)Events[showContextMenuEventObject];
            if (showContextMenuEventObject != null && handler != null)
                handler(this, e);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public string GetWordBeforeCaret()
        {
            int start = TextUtilities.FindPrevWordStart(Document, Caret.Offset);
            return Document.GetText(start, Caret.Offset - start);
        }

	}
}
